# gozerbot/generic.py
#
#

""" generic functions """

__copyright__ = 'this file is in the public domain'

import time
import sys
import re
import traceback
import Queue
import urllib
import urllib2
import socket
import random
import os
import sgmllib
import thread
import popen2

exceptionlist = []

def gethgrev():
    """ get mercurial revision """
    rev = None
    try:
        proces = popen2.Popen4('hg tip')
        data = proces.fromchild.readlines()
        if not proces.wait():
            rev = int(data[0].split(':')[1].strip())
    except Exception, ex:
        pass
    return rev

def getsvnrev():
    """ get subversion revision """
    rev = None
    try:
        proces = popen2.Popen4('svn info')
        data = proces.fromchild.readlines()
        if not proces.wait():
            for i in data:
                if i.startswith('Revision'):
                    rev = i.split()[1]
    except Exception, ex:
        pass
    return rev

def lockdec(lock):
    """ return locking descriptor locking on the provided lock """
    def locked(func):
        """ lock function func """
        def lockedfunc(*args, **kwargs):
            """ the locked function """
            lock.acquire()
            res = None
            try:
                try:
                    res = func(*args, **kwargs)
                except:
                    handle_exception()
            finally:
                lock.release()
            return res
        return lockedfunc
    return locked

popenwhitelist = re.compile('^[\w\.]+$') # only allow chars, numbers, _ and .
popenlock = thread.allocate_lock()

class PopenWhitelistError(Exception):

    def __init__(self, item):
        self.item = item

    def __str__(self):
        return self.item

def ommpopen(args, userargs=[]):
    for i in args:
        if not re.match(popenwhitelist, i):
            raise PopenWhitelistError(i)
    popenlock.acquire()
    try:
        proces = popen2.Popen4(args + userargs)
    except:
        popenlock.release()
        raise
    popenlock.release()
    return proces

def die():
    """ stop the bot """
    os.kill(os.getpid(), 9)

def reboot():
    """ reboot the bot """
    os.execl('./runbot', './runbot')

def calledfrom(frame):
    """ return plugin from which function is called """
    try:
        plugname = frame.f_back.f_code.co_filename
        name = plugname.split('/')[-1][:-3]
    except AttributeError:
        name = None
    del frame
    return name

def stripident(userhost):
    """ strip ident char from userhost """
    if not userhost:
        return None
    if userhost[0] in "~-+^":
        userhost = userhost[1:]
    elif userhost[1] == '=':
        userhost = userhost[2:]
    return userhost

def stripidents(ulist):
    """ strip ident char from list of userhosts """
    result = []
    for userhost in ulist:
        result.append(stripident(userhost))
    return result

def checkchan(item):
    """ check if chan <channel> is in string """
    chanre = re.search(' chan (\S+)', item)
    if chanre:
        chan = str(chanre.group(1))
        item = re.sub(' chan ' + chan, '', item)
        return (chan.lower(), item)

def handle_exception(ievent=None, log=True):
    """ print exception trace to sys.stderr """
    trace = traceback.extract_tb(sys.exc_traceback)
    result = ""
    exctype, excvalue = sys.exc_info()[:2]
    for i in trace:
        filename = i[0].split('/')[-1]
        linenr = i[1]
        func = i[2]
        result += "%s:%s %s | " % (filename, linenr, func)
    errormsg = "EXCEPTION: %s%s: %s" % (result, exctype, excvalue)
    exceptionlist.append(errormsg)
    if ievent:
        ievent.reply(errormsg)
    if log:
        traceback.print_exc(sys.stderr)
    return errormsg

def getlistensocket(listenip):
    """ scan for a free socket on listenip """
    port = 5000
    while 1:
        time.sleep(0.01)
        try:
            port += 1
            if ':' in listenip:
                sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
            else:
                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            sock.setblocking(1)
            if port > 65000:
                port = 5000
            sock.bind((listenip, port))
            sock.listen(1)
            return (port, sock)
        except Exception, ex:
            pass

leapfactor = float(6*60*60)/float(365*24*60*60)

def elapsedstring(nsec, ywd = None):
    """ return string of elapsed time """
    nsec = int(float(nsec))
    year = 365*24*60*60
    week = 7*24*60*60
    day = 24*60*60
    hour = 60*60
    minute = 60
    nsec -= nsec * leapfactor
    years = int(nsec/year)
    nsec -= years*year
    weeks = int(nsec/week)
    nsec -= weeks*week
    days = int(nsec/day)
    nsec -= days*day
    hours = int(nsec/hour)
    nsec -= hours*hour
    minutes = int(nsec/minute)
    sec = int(nsec - minutes*minute)   
    result = ''
    if (years > 1):
        result = str(years) + " years "
    if (years == 1):
        result = "1 year "
    if (weeks > 1):
        result += str(weeks) + " weeks "
    if (weeks == 1):
        result += "1 week "
    if (days > 1):
        if ywd:
            result += 'and '+ str(days) + " days"
        else:
            result += str(days) + " days "
    if (days == 1):
        if ywd:
            result += 'and 1 day'
        else:
            result += "1 day "
    if ywd:
        return result
    if (hours > 1):
        result += str(hours) + " hours "
    if (hours == 1):
        result += "1 hour "
    if (minutes > 1):
        result += str(minutes) + " minutes "
    if (min == 1):
        result += "1 minute "
    if sec == 0:
        if result:
            return result
        else:
            return 0
    if (sec == 1):
        if result:
            result += "and 1 second "
        else:
            result = "1 second"
    else:
        if result:
            result += "and " + str(sec) + " seconds"
        else: 
            result = str(sec) + " seconds"
    return result.strip()

def hourmin(ttime):
    """ extract hour:minutes from time """
    result = ""
    timeres = time.localtime(ttime)
    if timeres[3] < 10:
        result += "0" + str(timeres[3]) + ":"
    else:
        result += str(timeres[3]) + ":"
    if timeres[4] < 10:
        result += "0" + str(timeres[4])
    else:
        result += str(timeres[4])
    return result

timere = re.compile('(\S+)\s+(\S+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(\d+)')

bdmonths = ['Bo', 'Jan', 'Feb', 'Mar', ' Apr', 'May', 'Jun', 'Jul', 'Aug', \
'Sep', 'Oct', 'Nov', 'Dec']

def striptime(what):
    """ strip time indicators from string """
    what = str(what)
    what = re.sub('\S+-\S+-\S+', '', what)
    what = re.sub('\S+-\S+', '', what)
    what = re.sub('\S+:\S+', '', what)
    what = re.sub('\s+', ' ', what)
    return what

def today():
    """ get time for today 00:00 """
    if time.daylight:
        ttime = time.ctime(time.time() + int(time.timezone) + 3600)
    else:
        ttime = time.ctime(time.time() + int(time.timezone))
    matched = re.search(timere, ttime)
    if matched:
        temp = "%s %s %s" % (matched.group(3), matched.group(2), \
matched.group(7))
        timestring = time.strptime(temp, "%d %b %Y")
        result = time.mktime(timestring)
        return result

def strtotime(what):
    """ convert string to time """
    daymonthyear = 0
    hoursmin = 0
    try:
        # check for day-month-year
        dmyre = re.search('(\S+)-(\S+)-(\S+)', str(what))
        if dmyre:
            (day, month, year) = dmyre.groups()
            date = "%s %s %s" % (day, bdmonths[int(month)], year)
            daymonthyear = time.mktime(time.strptime(date, "%d %b %Y"))
        else:
            # check for day-month
            dmre = re.search('(\S+)-(\S+)', str(what))
            if dmre:
                (day, month) = dmre.groups()
                date = "%s %s %s" % (day, bdmonths[int(month)], \
time.localtime()[0])
                daymonthyear = time.mktime(time.strptime(date,"%d %b %Y"))
        # check for hours:minutes:seconds
        hmsre = re.search('(\S+):(\S+):(\S+)', str(what))
        if hmsre:
            hours = 60 * 60 * (int(hmsre.group(1)))
            hoursmin = hours  + int(hmsre.group(2)) * 60
            hms = hoursmin + int(hmsre.group(3))
        else:
            # check for hours:minutes
            hmre = re.search('(\S+):(\S+)', str(what))
            if hmre:
                hours = 60 * 60 * (int(hmre.group(1)))
                hms = hours  + int(hmre.group(2)) * 60
            else:
                hms = 0
        if not daymonthyear and not hms:
            return None
        if daymonthyear == 0:
            heute = today()
        else:
            heute = daymonthyear
        return heute + hms
    except ValueError:
        return None
    except Exception, ex:
        handle_exception()
        return None

def uurminsec(ttime):
    """ return hours:minutes:seconds string """
    result = ""
    timeres = time.localtime(ttime)
    if timeres[3] < 10:
        result += "0" + str(timeres[3]) + ":"
    else:
        result += str(timeres[3]) + ":"
    if timeres[4] < 10:
        result += "0" + str(timeres[4]) + ":"
    else:
        result += str(timeres[4]) + ":"
    if timeres[5] < 10:
        result += "0" + str(timeres[5])
    else:
        result += str(timeres[5])
    return result


def getdaymonth(ttime):
    """ return day-month tuple"""
    timestr = time.ctime(ttime)
    result = re.search(timere, timestr)
    if result:
        return (result.group(3), result.group(2))
    else:
        return (None, None)

def getdaymonthyear(ttime):
    """ return day-month-year tuple"""
    timestr = time.ctime(ttime)
    result = re.search(timere, timestr)
    if result:
        return (result.group(3), result.group(2), result.group[7])
    else:
        return (None, None, None)

def dmy(ttime):
    """ return day-month-year string"""
    timestr = time.ctime(ttime)
    result = re.search(timere, timestr)
    if result:
        return "%s %s %s" % (result.group(3), result.group(2), result.group(7))
    else:
        return None

def checkchan(item):
    """ check if chan <channel> is in string """ 
    chanre = re.search(' chan (\S+)', item)
    if chanre:
        chan = str(chanre.group(1))
        item = re.sub(' chan ' + chan, '', item)
        return (chan.lower(), item)

def getwho(bot, who):
    """ get userhost from bots userhost cache """
    who = who.lower()
    try:
        result = bot.userhosts[who]
        return result
    except KeyError:
        return None

def waitforuser(bot, nick):
    """ wait for user response """
    queue = Queue.Queue()
    waitnr = bot.privwait.register(nick, queue)
    result = queue.get()
    bot.privwait.delete(waitnr)
    return result

def waitforuserdcc(bot, nick):
    """ wait for user dcc response """
    queue = Queue.Queue()
    waitnr = bot.wait.register('DCC', nick, queue)
    result = queue.get()
    bot.wait.delete(waitnr)
    return result

class CBURLopener(urllib.FancyURLopener):

    """ our URLOpener """

    def __init__(self, *args):
        self.version = "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.2.1) \
Gecko/20030225"
        #self.version += " GOZERBOT see http://r8.cg.nu"
        urllib.FancyURLopener.__init__(self, *args)

def geturl(url):
    """ fetch an url """
    urllib._urlopener = CBURLopener()
    result = urllib.urlopen(url)
    tmp = result.read()
    result.close()
    return tmp

def geturl2(url):
    """ use urllib2 to fetch an url """
    result = urllib2.urlopen(url)
    tmp = result.read()
    result.close()
    return tmp

def getrandomnick():
    """ generate random nick """
    return "gb" + str(random.randint(0, 100))

class Stripper(sgmllib.SGMLParser):

    """ html stripper """

    def __init__(self):
        sgmllib.SGMLParser.__init__(self)

    def strip(self, some_html):
        """ strip html """
        self.theString = ""
        self.feed(some_html)
        self.close()
        return self.theString

    def handle_data(self, data):
        """ data handler """
        self.theString += data

def striphtml(txt):
    """ strip html from txt """
    stripper = Stripper()
    return stripper.strip(txt)

def waitforqueue(queue):
    """ wait for queue output """
    result = []
    while 1:
        try:
            res = queue.get(1, 15)
        except Queue.Empty:
            break
        if not res:
            break
        result.append(res)
    return result

def ircdecode(what):
    try:
        what = unicode(what)
        what = str(what.encode("ascii", "ignore"))
    except UnicodeError:
        try:
            what = str(what.decode("ascii", "ignore"))
        except UnicodeError:
            handle_exception()
            return
    return what
